package gbench.webapps.mymall.api.model.storage;

import static java.text.MessageFormat.format;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * FileSystemStorageService
 */
public abstract class FileSystemStorageService implements StorageService {

	/**
	 * FileSystemStorageService
	 *
	 * @param properties
	 */
	public FileSystemStorageService(final StorageProperties properties) {

		if (properties.getLocation().trim().length() == 0) {
			throw new StorageException("File upload location can not be Empty.");
		}

		this.rootLocation = Paths.get(properties.getLocation());
	}

	@Override
	public String store(final MultipartFile file) {
		String path = null;
		try {
			if (file.isEmpty()) {
				throw new StorageException("Failed to store empty file.");
			}
			final Path destinationFile = this.rootLocation.resolve(Paths.get(file.getOriginalFilename())).normalize()
					.toAbsolutePath();
			if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) {
				// This is a security check
				throw new StorageException("Cannot store file outside current directory.");
			}
			try (final InputStream inputStream = file.getInputStream()) {
				Files.copy(inputStream, destinationFile, StandardCopyOption.REPLACE_EXISTING);
				path = destinationFile.toAbsolutePath().toString().replace("\\", "/");
			}
		} catch (IOException e) {
			throw new StorageException("Failed to store file.", e);
		}
		return path;
	}

	/**
	 * 数据存储
	 *
	 * @param inputStream 输入流
	 * @param filename    文件名
	 */
	@Override
	public String store(final InputStream inputStream, final String filename) {
		String path = null;
		try {
			final Path destinationFile = this.rootLocation.resolve(Paths.get(filename)).normalize().toAbsolutePath();
			final var file = destinationFile.getParent().toFile();
			if (!file.exists()) {
				file.mkdirs();
			}
			System.out.println(format("destinationFile:{0}", destinationFile));
			if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) {
				// This is a security check
				throw new StorageException("Cannot store file outside current directory.");
			}
			Files.copy(inputStream, destinationFile, StandardCopyOption.REPLACE_EXISTING);
			path = destinationFile.toAbsolutePath().toString().replace("\\", "/");
		} catch (IOException e) {
			throw new StorageException("Failed to store file.", e);
		}
		return path;
	}

	@Override
	public Stream<Path> loadAll() {
		try {
			return Files.walk(this.rootLocation, 1).filter(path -> !path.equals(this.rootLocation))
					.map(this.rootLocation::relativize);
		} catch (IOException e) {
			throw new StorageException("Failed to read stored files", e);
		}

	}

	@Override
	public Path load(String filename) {
		return rootLocation.resolve(filename);
	}

	@Override
	public Resource loadAsResource(String filename) {
		try {
			Path file = load(filename);
			Resource resource = new UrlResource(file.toUri());
			if (resource.exists() || resource.isReadable()) {
				return resource;
			} else {
				throw new StorageFileNotFoundException("Could not read file: " + filename);

			}
		} catch (MalformedURLException e) {
			throw new StorageFileNotFoundException("Could not read file: " + filename, e);
		}
	}

	@Override
	public void deleteAll() {
		FileSystemUtils.deleteRecursively(rootLocation.toFile());
	}

	@Override
	public void init() {
		try {
			Files.createDirectories(rootLocation);
		} catch (IOException e) {
			throw new StorageException("Could not initialize storage", e);
		}
	}

	private final Path rootLocation;
}